package org.yunai.swjg.server.module.rep;

import org.slf4j.Logger;
import org.springframework.stereotype.Service;
import org.yunai.swjg.server.core.annotation.MainThread;
import org.yunai.swjg.server.core.annotation.SceneThread;
import org.yunai.swjg.server.core.constants.SceneConstants;
import org.yunai.swjg.server.core.service.*;
import org.yunai.swjg.server.module.activity.ActivityService;
import org.yunai.swjg.server.module.player.vo.Player;
import org.yunai.swjg.server.module.rep.callback.PlayerEnterRepCallback;
import org.yunai.swjg.server.module.rep.callback.PlayerSwitchRepToNormalSceneCallback;
import org.yunai.swjg.server.module.rep.msg.SysPlayerEnterRepSceneMessage;
import org.yunai.swjg.server.module.rep.template.RepTemplate;
import org.yunai.swjg.server.module.scene.core.*;
import org.yunai.swjg.server.module.scene.core.template.SceneTemplate;
import org.yunai.yfserver.common.LoggerFactory;
import org.yunai.yfserver.message.sys.SysInternalMessage;
import org.yunai.yfserver.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 副本场景Service<br />
 * 每个副本可以拥有多个线。<br />
 * 当玩家进入副本时，并且副本不存在或者副本已满，则新开该副本对应的{@link SceneRunner}。<br />
 * 当副本中没人时，则进行副本关闭。<br />
 * 场景的开和关都在主线程中进行。
 * User: yunai
 * Date: 13-5-12
 * Time: 下午7:19
 */
@Service
public class RepSceneService extends AbstractSceneService<RepScene> {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerFactory.Logger.rep, RepSceneService.class);

    /**
     * 检查空闲线程周期，单位（毫秒） TODO 太频繁 log很烦 先设置大点
     */
//    private static final int CLEAR_IDLE_SCENE_PERIOD = 20000 * 1000;
    private static final int CLEAR_IDLE_SCENE_PERIOD = 20000;

    @Resource
    private OnlineContextService onlineContextService;
    @Resource
    private GameMessageProcessor gameMessageProcessor;
    @Resource
    private GameExecutorService gameExecutorService;
    @Resource
    private ActivityService activityService;

    /**
     * 场景线Runner服务Map<br />
     * <场景编号-线编号,场景>
     */
    private final Map<Integer, List<SceneRunner<RepScene>>> sceneRunners;
    /**
     * 线自增数<br />
     * 目前该变量只会在主线程访问，所以不设置volatile
     */
    private int lineSequence = 1;
    /**
     * 场景数量<br />
     * 目前该变量只会在主线程访问，所以不设置volatile
     */
    private int sceneCount = 0;

    private final Runnable CLEAR_IDLE_SCENE_RUNNER = new Runnable() {

        @Override
        public void run() {
            gameMessageProcessor.put(new SysInternalMessage() { // TODO 活动副本不自动关闭

                @Override
                public void execute() {
//                    if (sceneCount == 0) { // TODO sceneCount可以不维护了
//                        return;
//                    }
                    if (sceneRunners.isEmpty()) {
                        return;
                    }
                    Iterator<List<SceneRunner<RepScene>>> iterator = sceneRunners.values().iterator();
                    do {
                        Iterator<SceneRunner<RepScene>> subIterator = iterator.next().iterator();
                        if (!subIterator.hasNext()) {
                            iterator.remove();
                            continue;
                        }
                        do {
                            RepScene scene = subIterator.next().getScene();
                            if (scene.isEmpty() && scene.isMessageEmpty()) {
                                if (scene.getSceneId() == SceneConstants.SCENE_BOSS_ACTIVITY_A) {
                                    if (!activityService.getBossActivityA().isEnd()) {
                                        continue;
                                    }
                                }
                                LOGGER.info("[CLEAR_IDLE_SCENE_RUNNER] [scene({}) line({}) is clear].", scene.getSceneId(), scene.getLine());
                                subIterator.remove();
                                sceneCount--;
                            }
                        } while (subIterator.hasNext());
                    } while (iterator.hasNext());
                }

                @Override
                public short getCode() {
                    return 0;
                }
            });
        }

    };

    public RepSceneService() {
        this.sceneRunners = new ConcurrentHashMap<>();
    }

    @PostConstruct
    public void init() {
        LOGGER.info("[init] [START CLEAR_IDLE_SCENE_RUNNER BEGIN].");
        gameExecutorService.scheduleTask(CLEAR_IDLE_SCENE_RUNNER, CLEAR_IDLE_SCENE_PERIOD);
        LOGGER.info("[init] [START CLEAR_IDLE_SCENE_RUNNER SUCCESS].");
    }

    /**
     * [主线程]玩家从普通场景切换到副本场景<br />
     * 首先，设置玩家游戏状态为{@link GamingState#SWITCH_NORMAL_SCENE},玩家离开该场景<br />
     * 之后，玩家通过{@link PlayerEnterRepCallback}来实现进入场景
     *
     * @param online 在线信息
     * @param repId  副本编号
     */
    @MainThread
    public void onPlayerSwitchRep(Online online, Integer repId) {
        AbstractScene scene = online.getScene();
        if (scene == null) { // 此时，场景是存在的。
            LOGGER.error("[onPlayerSwitchRep] [online:{} switch rep failure] [online.scene isn't exist].", online.getPlayer().getId());
            return;
        }
        // TODO 副本进入验证，比如说等级，体力不够等等。提示客户端
        // 设置玩家游戏状态为GamingState.SWITCH_REP_SCENE，玩家离开该场景
        online.setGamingState(GamingState.SWITCH_REP_SCENE);
        super.onPlayerLeaveScene(online, new PlayerEnterRepCallback(online.getPlayer().getId(), repId));
    }

    /**
     * [主线程]进入副本<br />
     * 若加入成功，则给｛副本场景消息队列｝发送消息来进行之后的逻辑
     *
     * @param online 在线信息
     * @param repId  场景编号
     */
    @MainThread
    public boolean onPlayerEnterRep(Online online, Integer repId) {
        AbstractScene scene = online.getScene();
        Player player = online.getPlayer();
        if (scene != null) { // 此时，玩家已经从原来场景退出，所以该变量会为null
            LOGGER.error("[onPlayerEnterRep] [online:{} enter rep failure] [online.scene exists].", player.getId());
            return false;
        }
        RepScene rep = getOrCreateRep(repId);
        if (rep == null) {
            LOGGER.error("[onPlayerEnterRep] [online:{} enter rep failure] [rep({}) not exists].", player.getId(), repId);
            return false;
        }
        // TODO 副本进入验证，比如说等级，体力不够等等。直接网络断开
        // 设置玩家所在场景
        online.setScene(rep);
        player.markScenePreInfo();
        player.enterScene(repId, (short) 5, (short) 12); // TODO 这里坐标临时下
        rep.addOnline(online); // 将玩家添加到副本场景玩家集合中
        // 给该副本发送进入的消息
        rep.putMessage(new SysPlayerEnterRepSceneMessage(online.getPlayer().getId(), rep.getSceneId(), rep.getLine()));
        return true;
    }

    /**
     * [场景线程]处理玩家进入，并返回是否进入成功<br />
     * 获得不到Online或者Scene会导致进入场景失败
     *
     * @param playerId 玩家编号
     * @param sceneId    进入场景编号
     * @param line     进入场景线号
     * @return 进入场景是否成功
     */
    @SceneThread
    public boolean handleEnterRep(Integer playerId, Integer sceneId, Integer line) {
        Online online = onlineContextService.getPlayer(playerId);
        if (online == null) {
            return false;
        }
        RepScene rep = getScene(sceneId, line);
        if (rep == null) {
            LOGGER.error("[handleEnterRep] [online:{} enter rep failure] [scene:{} line:{} is not exist].",
                    online.getPlayer().getId(), sceneId, line);
            return false;
        }
        // 副本场景通知
        rep.onPlayerEnter(online);
        return true;
    }

    @MainThread
    public boolean onPlayerSwitchRepToNormalScene(Online online) {
        AbstractScene scene = online.getScene();
        if (scene == null) {
            LOGGER.error("[onPlayerSwitchRepToNormalScene] [online:{} switch rep to scene failure] [online.scene not exists].", online.getPlayer().getId());
            return false;
        }
        // 设置玩家游戏状态为GamingState.SWITCH_NORMAL_SCENE，玩家离开该场景
        online.setGamingState(GamingState.SWITCH_NORMAL_SCENE);
        super.onPlayerLeaveScene(online, new PlayerSwitchRepToNormalSceneCallback(online.getPlayer().getId()));
        return true;
    }

    /**
     * [主线程] 获得副本，若副本不存在，则进行副本生成.
     *
     * @param repId 副本编号
     * @return 副本
     */
    private RepScene getOrCreateRep(Integer repId) {
        RepTemplate repTemplate = RepTemplate.get(repId);
        if (repTemplate == null) {
            return null;
        }
        Integer sceneId = repTemplate.getSceneTemplate().getId();
        List<SceneRunner<RepScene>> runners = sceneRunners.get(sceneId);
        if (CollectionUtils.isEmpty(runners)) {
            sceneRunners.put(sceneId, runners = new ArrayList<>(1));
        }
        SceneTemplate sceneTemplate = repTemplate.getSceneTemplate();
        RepScene rep = null;
        if (sceneTemplate.getType() == SceneDef.Type.REP) {
            rep = (RepScene) SceneFactory.createScene(repTemplate.getSceneTemplate());
            rep.setLine(lineSequence++);
            runners.add(new SceneRunner<>(rep));
            sceneCount++;
        } else if (sceneTemplate.getType() == SceneDef.Type.ACTIVITY) { // 活动副本不自动创建
            if (runners.size() == 0) {
//                rep = (ActivityRepScene) SceneFactory.createScene(repTemplate.getSceneTemplate());
//                rep.setLine(lineSequence++);
//                runners.give(new SceneRunner<>(rep));
//                sceneCount++;
                return null;
            } else {
                rep = runners.get(runners.size() - 1).getScene();
            }
        }
        return rep;
    }

    public ActivityRepScene createActivityRep(Integer repId) {
        RepTemplate repTemplate = RepTemplate.get(repId);
        if (repTemplate == null || repTemplate.getSceneTemplate().getType() != SceneDef.Type.ACTIVITY) {
            return null;
        }
        Integer sceneId = repTemplate.getSceneTemplate().getId();
        List<SceneRunner<RepScene>> runners = sceneRunners.get(sceneId);
        if (CollectionUtils.isEmpty(runners)) {
            sceneRunners.put(sceneId, runners = new ArrayList<>(1));
        }
        RepScene rep = (ActivityRepScene) SceneFactory.createScene(repTemplate.getSceneTemplate());
        rep.setLine(lineSequence++);
        runners.add(new SceneRunner<>(rep));
        // log 下
        sceneCount++;
        return (ActivityRepScene) rep;
    }

    /**
     * @param sceneId 场景编号
     * @param line    场景线
     * @return 副本
     */
    @Override
    protected RepScene getScene(Integer sceneId, Integer line) {
        List<SceneRunner<RepScene>> runners = sceneRunners.get(sceneId);
        if (runners == null) {
            return null;
        }
        for (SceneRunner<RepScene> runner : runners) {
            if (runner.getScene().getLine().equals(line)) {
                return runner.getScene();
            }
        }
        return null;
    }

    /**
     * @return 所有的Runner
     */
    public List<SceneRunner<RepScene>> getAllSceneRunners() {
        List<SceneRunner<RepScene>> runners = new ArrayList<>(sceneRunners.size());
        for (Collection<SceneRunner<RepScene>> subRunners : sceneRunners.values()) {
            if (subRunners.size() == 0) {
                continue;
            }
            for (SceneRunner<RepScene> runner : subRunners) {
                runners.add(runner);
            }
        }
        return runners;
    }

    /**
     * 将玩家回到进入副本前的场景<br />
     * 该方法只改变内存中数据。
     *
     * @param online 在线缓存
     */
    @MainThread
    public void gotoBeforeScene(Online online) {
        Player player = online.getPlayer();
        Integer sceneId = player.getScenePreId();
        Short sceneX = player.getScenePreX();
        Short sceneY = player.getScenePreY();
        player.clearScenePreInfo(); // 清理掉上一次场景信息
        player.enterScene(sceneId, sceneX, sceneY);
    }
}
